package com.template.service.impl;

import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.template.common.annotation.CacheRemove;
import com.template.common.lang.Const;
import com.template.domain.entity.SysMenu;
import com.template.domain.entity.SysRole;
import com.template.domain.entity.RoleMenu;
import com.template.mapper.RoleMapper;
import com.template.service.SysMenuService;
import com.template.service.RoleMenuService;
import com.template.service.SysRoleService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import static com.template.domain.entity.table.RoleMenuTableDef.ROLE_MENU;
import static com.template.domain.entity.table.SysMenuTableDef.SYS_MENU;

/**
 * 服务层实现。
 *
 * @author dong
 * @since 2023-09-03
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SysRoleServiceImpl extends ServiceImpl<RoleMapper, SysRole> implements SysRoleService {

    private final SysMenuService sysMenuService;

    private final RoleMenuService roleMenuService;

    @Override
    @CacheRemove({Const.Cache.AUTHORITY + "*"})
    public boolean save(SysRole entity) {
        return super.save(entity);
    }

    @Override
    @CacheRemove({Const.Cache.AUTHORITY + "*"})
    public boolean updateById(SysRole entity) {
        return super.updateById(entity);
    }

    @Override
    @CacheRemove({Const.Cache.AUTHORITY + "*"})
    public boolean removeById(Serializable id) {
        return super.removeById(id);
    }

    @Override
    public Set<Long> getRolePermsId(Long roleId) {
        if (roleId == null) return new HashSet<>();

        List<SysMenu> list = sysMenuService.queryChain()
                .select(SYS_MENU.ID.as(SysMenu::getId), SYS_MENU.PARENT_ID)
                .from(ROLE_MENU).leftJoin(SYS_MENU).on(ROLE_MENU.MENU_ID.eq(SYS_MENU.ID))
                .where(ROLE_MENU.ROLE_ID.eq(roleId))
                .list();
        Set<Long> permsIds = list.stream().map(SysMenu::getId).collect(Collectors.toSet());
        Set<Long> parentIds = list.stream().map(SysMenu::getParentId).filter(Objects::nonNull).collect(Collectors.toSet());
        // 求差集
        Set<Long> diffParentIds = new HashSet<>() {{
            addAll(parentIds);
            removeAll(permsIds);
        }};
        getParentPermsIds(permsIds, diffParentIds);
        return permsIds;
    }

    @Override
    @Transactional
    @CacheRemove({Const.Cache.AUTHORITY + "*"})
    public void assignPerms(Long roleId, List<Long> menuIds) {
        if (roleId == null || menuIds == null) return;
        // 删除所有角色权限
        roleMenuService.remove(roleMenuService.query().where(ROLE_MENU.ROLE_ID.eq(roleId)));
        // 添加新的角色权限
        if (menuIds.size() == 0) return;
        List<RoleMenu> list = menuIds.stream()
                .filter(Objects::nonNull)
                .map(menuId -> new RoleMenu().setRoleId(roleId).setMenuId(menuId))
                .toList();
        roleMenuService.saveBatch(list);
    }

    private void getParentPermsIds(Set<Long> permsIds, Set<Long> parentIds) {
        if (parentIds.size() == 0) return;
        List<SysMenu> list = sysMenuService.queryChain()
                .select(SYS_MENU.ID, SYS_MENU.PARENT_ID)
                .where(SYS_MENU.ID.in(parentIds))
                .list();
        Set<Long> selectPermsIds = list.stream().map(SysMenu::getId).collect(Collectors.toSet());
        Set<Long> selectParentIds = list.stream().map(SysMenu::getParentId).filter(Objects::nonNull).collect(Collectors.toSet());
        // 将查询到的权限id添加到用户权限列表中
        permsIds.addAll(selectPermsIds);
        // 若父级权限id在用户权限列表中不存在，则继续向上查询父级权限
        selectParentIds.removeAll(permsIds);
        // 将父级权限id添加至用户权限列表中
        permsIds.addAll(selectParentIds);
        getParentPermsIds(permsIds, selectParentIds);
    }

}
